/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package main;

import helper.Utilities;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.Handshake;
import model.Message;
import model.Torrent;

/**
 *
 * @author Hallucinogen
 */
public class PeerDownloader extends Thread {

    public PeerDownloader(Peer peer, Torrent torrent) {
        m_Peer      = peer;
        m_Running   = false;
        m_Torrent   = torrent;
        m_Available = m_Torrent.compareDownloadedFile(m_Torrent.getResultPath());
    }

    @Override
    public void run() {
        //kirim handshake
        Socket socket = null;
        Handshake hs  = new Handshake();
        hs.setFileName(m_Torrent.getFileName());
        StringBuilder sb = new StringBuilder();
        try {
            for (int i = 0; i < m_Peer.getResult().getPeers().size(); ++i) {
                Peer otherPeer = m_Peer.getResult().getPeers().get(i);

                if (otherPeer.getAddress().getAddress().getHostAddress().equals(InetAddress.getLocalHost().getHostAddress())) continue;
                System.out.println("[Peer Downloader] Establishing socket at " + otherPeer.getAddress().getAddress().getHostAddress());
                socket = new Socket(otherPeer.getAddress().getAddress().getHostAddress(), Utilities.PEER_PORT);
                socket.setSoTimeout(5000);
                System.out.println("[Peer Downloader] Connection established at " + otherPeer.getAddress().getAddress().getHostAddress());
				
                // cek apakah dia nerima handshake
                BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

                bos.write(hs.toByte());

                bos.flush();

                BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
                Message message = null;
                boolean readBitfield = false;
                int input = -1;
                while (!readBitfield && ((input = in.read()) != -1)) {
                    /** baca message length */
                    int len = input;
                    String temp = "" + (char)input;
                    for (int j = 1; j < 4; ++j){
                        input = in.read();
                        len = len * 256 + input;
                        temp = temp + (char)input;
                    }
                    
                    sb = new StringBuilder();
                    /** baca sisanya */
                    for (int j = 0; j < len; ++j) {
                        sb.append((char)in.read());
                    }
                    
                    message = Message.fromString(temp + sb.toString());
                    System.out.println(message.getType());
                    if (message.getType() == Message.MESSAGE_BITFIELD) {
                        readBitfield = true;
                    }
                }

                System.out.println("[Peer Downloader] bitfield read");
                boolean[] otherAvail = new boolean[m_Available.length];
                for (int j = 0; j < message.getContent().length(); ++j) {
                    if (message.getContent().charAt(j) == 0) {
                        otherAvail[j] = false;
                    } else if (message.getContent().charAt(j) == 1) {
                        otherAvail[j] = true;
                    }
                }

                for (int j = 0; j < m_Available.length; ++j) {
                    if (m_Available[j]) continue;
                    if (!otherAvail[j]) continue;

                    int index = j;
                    int begin = 0;
                    int length = (int)m_Torrent.getLength();

                    sb = new StringBuilder();

                    // masukin index
                    sb.append((char)((index >> 24) % 256));
                    sb.append((char)((index >> 16) % 256));
                    sb.append((char)((index >> 8) % 256));
                    sb.append((char)(index % 256));


                    // masukin begin
                    sb.append((char)((begin >> 24) % 256));
                    sb.append((char)((begin >> 16) % 256));
                    sb.append((char)((begin >> 8) % 256));
                    sb.append((char)(begin % 256));


                    // masukin index
                    sb.append((char)((length >> 24) % 256));
                    sb.append((char)((length >> 16) % 256));
                    sb.append((char)((length >> 8) % 256));
                    sb.append((char)(length % 256));

                    Message req = new Message(sb.toString().length() + 1, Message.MESSAGE_REQUEST, sb.toString());


                    System.out.println("[Peer Downloader] request piece");
                    bos.write(req.toByte());

                    bos.flush();

                    // baca lagi
                    input = -1;
                    boolean readPiece = false;
                    while (!readPiece && ((input = in.read()) != -1)) {
                        /** baca message length */
                        int len = input;
                        String temp = "" + (char)input;
                        for (int k = 1; k < 4; ++k){
                            input = in.read();
                            len = len * 256 + input;
                            temp = temp + (char)input;
                        }

                        sb = new StringBuilder();
                        /** baca sisanya */
                        for (int k = 0; k < len; ++k) {
                            sb.append((char)in.read());
                        }

                        message = Message.fromString(temp + sb.toString());
                        if (message.getType() == Message.MESSAGE_PIECE) {
                            readPiece = true;
                        }
                    }


                    System.out.println("[Peer Downloader] new piece read");
                    // wah berhasil baca response, mari kita cek hasilnya
                    index = message.getContent().charAt(0);
                    index = index * 256 + message.getContent().charAt(1);
                    index = index * 256 + message.getContent().charAt(2);
                    index = index * 256 + message.getContent().charAt(3);

                    begin = message.getContent().charAt(4);
                    begin = begin * 256 + message.getContent().charAt(5);
                    begin = begin * 256 + message.getContent().charAt(6);
                    begin = begin * 256 + message.getContent().charAt(7);

                    byte[] bin = new byte[message.getContent().length() - 8];

                    for (int k = 8; k < message.getContent().length(); ++k)
                        bin[k - 8] = (byte)message.getContent().charAt(k);

                    
                    System.out.println("[Peer Downloader] saving piece to file");
                    m_Torrent.savePieceToResult(index, bin);
                    try {
                        System.out.println("[Peer Downloader] piece sesuai dengan torrent ? " + Utilities.convertToSHA1(bin).equals(m_Torrent.getPieces().get(index)));
                    } catch (Exception ex) {
                        
                    }
                }
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            
        } finally {
            try {
                if (socket != null)
                    socket.close();
            } catch (IOException iEx) {
                
            }
        }

        /*try {
            BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
            String userInput;
            userInput = hs.toString();
            System.out.println("Sampai sini\n");

            while ((userInput = stdIn.readLine()) != null) {
                out.println(userInput);
                System.out.println("echo: " + in.readLine());
            }

            System.out.println("Dan di sini\n");
            out.close();
            in.close();
            stdIn.close();
            sock.close();
        } catch (IOException ex) {
            Logger.getLogger(PeerDownloader.class.getName()).log(Level.SEVERE, null, ex);
        }*/

        // menerima bitfiled
        m_Running = true;
        while (m_Running) {
            if (System.currentTimeMillis() - m_Peer.getLastRequest() < Utilities.KEEP_ALIVE) {
                Message message = new Message();
                /// TODO : ngasih tau kalo masih idup dengan mengirim keep alive
            }


            // komunikasi message

            /*try {
                /// TODO : request ke peer lain
                m_Peer.setLastRequest(System.currentTimeMillis());
            } catch (IOException ex){
                // do nothing juga
            }*/
        }
    }

    public Peer getPeer(){
        return m_Peer;
    }

    public boolean isRunning(){
        return m_Running;
    }

    public void setRunning(boolean running){
        m_Running = running;
    }

    public static void main(String[] args) {
        Peer p = new Peer("127.0.0.1");
    }

    private Peer m_Peer;
    private boolean m_Running;
    private Torrent m_Torrent;
    private boolean[] m_Available;
}

class ConnectionDownload implements Runnable {
    private Socket socket;

    public ConnectionDownload(Socket socket) {
        this.socket = socket;

        Thread t = new Thread(this);
        t.start();
    }

    public void run() {
        try
        {
            //
            // Read a message sent by client application
            //
            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
            String message = (String) ois.readObject();
            System.out.println("Message Received: " + message);

            //
            // Send a response information to the client application
            //
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            oos.writeObject("Hi...");

            ois.close();
            oos.close();
            socket.close();

            System.out.println("Waiting for client message...");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
